package com.sec.android.app.myfiles.util;

import android.content.Context;

import com.sec.android.app.myfiles.MyFilesTestRunner;
import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.TestInput;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.shadow.android.ShadowStatFs;
import com.sec.android.app.myfiles.shadow.android.ShadowStorageManager;
import com.sec.android.app.myfiles.shadow.sem.ShadowSemPersonaManager;
import com.sec.android.app.myfiles.shadow.sem.ShadowSemPrivateModeManager;

import junit.framework.Assert;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;

/**
 * Created by zhitu.chen on 8/26/16.
 */

@RunWith(MyFilesTestRunner.class)
public class testStorageMonitor {
    Context mContext;

    @Before
    public void setUp() throws Exception {
        ShadowSemPersonaManager.init();
        ShadowSemPrivateModeManager.init();
        mContext = RuntimeEnvironment.application;
    }

    @After
    public void tearDown() throws Exception {
        ShadowStorageManager.clearStorageVolumes();
    }

    @Test
    public void testGetExtSDCardPath() {
        Assert.assertEquals(AppConstants.EXT_SD_CARD_ROOT, StorageMonitor.getExtSDCardPath());
    }

    @Test
    public void testStorageRoot() {
        String path = AppConstants.StoragePath.INTERNAL_ROOT;
        Assert.assertTrue(StorageMonitor.isStorageRoot(path));

        path = AppConstants.EXT_SD_CARD_ROOT;
        ShadowStorageManager.addStorageVolumes(path);
        Assert.assertTrue(StorageMonitor.isStorageRoot(path));

        path = AppConstants.StoragePath.PRIVATE_ROOT;
        Assert.assertTrue(StorageMonitor.isStorageRoot(path));

        path = "/TestRoot/TestDir";
        Assert.assertFalse(StorageMonitor.isStorageRoot(path));

    }

    @Test
    public void testGetDisplayName() {
        TestInput<Integer>[] testList = TestInput.createTestData(
                new TestInput<>(R.string.my_device, AppConstants.StoragePath.ROOT),
                new TestInput<>(UiUtils.getInternalStorageRootResId(), AppConstants.StoragePath.INTERNAL_ROOT),
                new TestInput<>(R.string.sd_card, AppConstants.EXT_SD_CARD_ROOT),
                new TestInput<>(R.string.private_storage, AppConstants.StoragePath.PRIVATE_ROOT),
                new TestInput<>(R.string.subtitle_recent, AppConstants.StoragePath.RECENT_FILES),
                new TestInput<>(R.string.images, AppConstants.StoragePath.CATEGORY_IMAGE),
                new TestInput<>(R.string.videos, AppConstants.StoragePath.CATEGORY_VIDEO),
                new TestInput<>(R.string.audio, AppConstants.StoragePath.CATEGORY_AUDIO),
                new TestInput<>(R.string.documents, AppConstants.StoragePath.CATEGORY_DOCUMENT),
                new TestInput<>(R.string.downloads, AppConstants.StoragePath.DOWNLOADS),
                new TestInput<>(R.string.google_drive, AppConstants.StoragePath.GOOGLE_DRIVE_FOLDER),
                new TestInput<>(R.string.samsung_drive, AppConstants.StoragePath.SAMSUNG_DRIVE_FOLDER));


        for (TestInput<Integer> testInput : testList) {
            String actual = StorageMonitor.getDisplayName(mContext, (String) testInput.get(0));
            String excepted = mContext.getString(testInput.expected());
            Assert.assertEquals(excepted, actual);
        }
    }

    static class ExpectedDisplayName {
        ExpectedDisplayName(String path, int resNameId) {
            mPath = path;
            mResNameId = resNameId;
        }

        String mPath;
        int mResNameId;
    }


    static class ExpectedStorageState {
        ExpectedStorageState(boolean isSdCardMounted, boolean isUsbStorageMounted,
                             String storagePath, String sdCardPath,
                             String usb_A_Path, String usb_B_Path, String usb_C_Path) {
            mIsSdCardMounted = isSdCardMounted;
            mIsUsbStorageMounted = isUsbStorageMounted;
            mStoragePath = storagePath;
            mSdCardPath = sdCardPath;
            mUsb_A_Path = usb_A_Path;
            mUsb_B_Path = usb_B_Path;
            mUsb_C_Path = usb_C_Path;
        }

        boolean mIsSdCardMounted;
        boolean mIsUsbStorageMounted;
        String mStoragePath;
        String mSdCardPath;
        String mUsb_A_Path;
        String mUsb_B_Path;
        String mUsb_C_Path;
        String mPrivatePath = AppConstants.StoragePath.PRIVATE_ROOT;
    }

    private void assertStorageState(ExpectedStorageState expected) {
        Assert.assertEquals(expected.mIsSdCardMounted, StorageMonitor.isSdCardMounted());
        Assert.assertEquals(expected.mIsUsbStorageMounted, StorageMonitor.isUsbStorageMounted());

        Assert.assertEquals(expected.mStoragePath, StorageMonitor.getStoragePath(mContext, StorageMonitor.iStorageType.INTERNAL));
        Assert.assertEquals(StorageMonitor.iStorageType.INTERNAL, StorageMonitor.getMatchedStorageType(expected.mStoragePath));
        Assert.assertTrue(StorageMonitor.isStorageMounted(mContext, StorageMonitor.iStorageType.INTERNAL));
        Assert.assertTrue(StorageMonitor.isStorageMounted(mContext, expected.mStoragePath));

        Assert.assertEquals(AppConstants.StoragePath.PRIVATE_ROOT, StorageMonitor.getStoragePath(mContext, StorageMonitor.iStorageType.EXTERNAL_PRIVATE_MODE));
        Assert.assertEquals(StorageMonitor.iStorageType.EXTERNAL_PRIVATE_MODE, StorageMonitor.getMatchedStorageType(AppConstants.StoragePath.PRIVATE_ROOT));
        Assert.assertTrue(StorageMonitor.isStorageMounted(mContext, StorageMonitor.iStorageType.EXTERNAL_PRIVATE_MODE));
        Assert.assertTrue(StorageMonitor.isStorageMounted(mContext, AppConstants.StoragePath.PRIVATE_ROOT));

        Assert.assertEquals(expected.mIsSdCardMounted, StorageMonitor.isSdCardMounted());
        Assert.assertEquals(expected.mSdCardPath, StorageMonitor.getStoragePath(mContext, StorageMonitor.iStorageType.EXTERNAL_SD));
        if (expected.mSdCardPath != null) {
            Assert.assertEquals(StorageMonitor.iStorageType.EXTERNAL_SD, StorageMonitor.getMatchedStorageType(expected.mSdCardPath));
        }

        Assert.assertEquals(expected.mIsSdCardMounted, StorageMonitor.isStorageMounted(mContext, StorageMonitor.iStorageType.EXTERNAL_SD));
        Assert.assertEquals(expected.mIsSdCardMounted, StorageMonitor.isStorageMounted(mContext, expected.mSdCardPath));


        Assert.assertEquals(expected.mIsUsbStorageMounted, StorageMonitor.isUsbStorageMounted());


        testUsbInfo(expected.mUsb_A_Path != null, "/mnt/usbA", StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_A);
        testUsbInfo(expected.mUsb_B_Path != null, "/mnt/usbB", StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_B);
        testUsbInfo(expected.mUsb_C_Path != null, "/mnt/usbC", StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_C);


        Assert.assertEquals(expected.mUsb_A_Path, StorageMonitor.getStoragePath(mContext, StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_A));
        Assert.assertEquals(expected.mUsb_B_Path, StorageMonitor.getStoragePath(mContext, StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_B));
        Assert.assertEquals(expected.mUsb_C_Path, StorageMonitor.getStoragePath(mContext, StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_C));
    }

    private void testUsbInfo(boolean bExpectedMount, String path, int storageType) {
        int expectedType = StorageMonitor.iStorageType.UNKNOWN;
        String expectedPath = null;
        String expectedName = null;
        if (bExpectedMount) {
            expectedType = storageType;
            expectedPath = path;
            expectedName = mContext.getString(R.string.usb_storage) + " " + FileRecord.getName(path);
        }

        Assert.assertEquals(path, bExpectedMount, StorageMonitor.isUsbStorageMounted(storageType));
        Assert.assertEquals(path, bExpectedMount, StorageMonitor.isStorageMounted(mContext, storageType));
        Assert.assertEquals(path, bExpectedMount, StorageMonitor.isStorageMounted(mContext, path));

        if (expectedPath != null) {
            if (expectedPath.equals(StorageMonitor.getStoragePath(mContext, storageType))) {

            } else {
                int i = 0;
                i++;
                StorageMonitor.updateSubSystemStorageState(mContext);
            }
        }
        //Assert.assertEquals(expectedPath, StorageMonitor.getStoragePath(mContext, storageType));
        Assert.assertEquals(expectedType, StorageMonitor.getMatchedStorageType(path));
        Assert.assertEquals(expectedName, StorageMonitor.getUsbStorageName(mContext, storageType));
        Assert.assertEquals(bExpectedMount, StorageMonitor.isUsbRoot(path));
        if (path != null) {
            Assert.assertFalse(StorageMonitor.isUsbRoot(path + "/data"));
        }
    }

    private void _testUpdateSubSystemStorageState() {
        ShadowStorageManager.clearStorageVolumes();

        //no media
        ExpectedStorageState expected = new ExpectedStorageState(false, false, AppConstants.StoragePath.INTERNAL_ROOT, null, null, null, null);
        StorageMonitor.updateSubSystemStorageState(mContext);
        assertStorageState(expected);

        //SD card inserted
        expected = new ExpectedStorageState(true, false, AppConstants.StoragePath.INTERNAL_ROOT, AppConstants.EXT_SD_CARD_ROOT, null, null, null);
        ShadowStorageManager.addStorageVolumes(AppConstants.EXT_SD_CARD_ROOT);
        StorageMonitor.updateSubSystemStorageState(mContext);
        assertStorageState(expected);

        //USB A inserted
        expected = new ExpectedStorageState(true, true, AppConstants.StoragePath.INTERNAL_ROOT, AppConstants.EXT_SD_CARD_ROOT, "/mnt/usbA", null, null);
        ShadowStorageManager.addStorageVolumes("/mnt/usbA");
        StorageMonitor.updateSubSystemStorageState(mContext);
        assertStorageState(expected);


        //USB B inserted
        expected = new ExpectedStorageState(true, true, AppConstants.StoragePath.INTERNAL_ROOT, AppConstants.EXT_SD_CARD_ROOT, "/mnt/usbA", "/mnt/usbB", null);
        ShadowStorageManager.addStorageVolumes("/mnt/usbB");
        StorageMonitor.updateSubSystemStorageState(mContext);
        assertStorageState(expected);

        //USB C inserted
        expected = new ExpectedStorageState(true, true, AppConstants.StoragePath.INTERNAL_ROOT, AppConstants.EXT_SD_CARD_ROOT, "/mnt/usbA", "/mnt/usbB", "/mnt/usbC");
        ShadowStorageManager.addStorageVolumes("/mnt/usbC");
        StorageMonitor.updateSubSystemStorageState(mContext);
        assertStorageState(expected);
    }

    @Test
    public void testUpdateSubSystemStorageState() {
        ShadowSemPersonaManager.sRetNull = true;
        _testUpdateSubSystemStorageState();

        ShadowSemPersonaManager.sRetException = true;
        _testUpdateSubSystemStorageState();

        ShadowSemPersonaManager.sRetNull = false;
        ShadowSemPersonaManager.sRetException = false;
        _testUpdateSubSystemStorageState();
    }

    @Test
    public void testStartWithUsbRoot() {
        String testList[] = {
                "/mnt/usbA",
                "/mnt/usbB",
                "/mnt/usbC",
                "/mnt/usbD",
        };

        StorageMonitor.updateSubSystemStorageState(mContext);

        for (String path : testList) {
            Assert.assertFalse(path, StorageMonitor.startWithUsbRoot(path));
            ShadowStorageManager.addStorageVolumes(path);
            StorageMonitor.updateSubSystemStorageState(mContext);
            Assert.assertTrue(path, StorageMonitor.startWithUsbRoot(path));
        }
    }

    @Test
    public void testInternalPath() {
        Assert.assertTrue(StorageMonitor.isInternalPath(AppConstants.StoragePath.INTERNAL_ROOT));
        Assert.assertFalse(StorageMonitor.isInternalPath(AppConstants.StoragePath.INTERNAL_ROOT + "abc"));
        Assert.assertTrue(StorageMonitor.isInternalPath(AppConstants.StoragePath.INTERNAL_ROOT + "/abc"));
    }

    @Test
    public void testIsSdCardPath() {
        //before sd insert
        StorageMonitor.updateSubSystemStorageState(mContext);
        Assert.assertTrue(StorageMonitor.isSdCardPath(AppConstants.EXT_SD_CARD_ROOT));
        Assert.assertFalse(StorageMonitor.isSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "abc"));
        Assert.assertTrue(StorageMonitor.isSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "/abc"));

        //after sd insert
        ShadowStorageManager.addStorageVolumes(AppConstants.EXT_SD_CARD_ROOT);
        StorageMonitor.updateSubSystemStorageState(mContext);
        Assert.assertTrue(StorageMonitor.isSdCardPath(AppConstants.EXT_SD_CARD_ROOT));
        Assert.assertTrue(StorageMonitor.isSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "/abc"));
        Assert.assertFalse(StorageMonitor.isSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "abc"));
    }

    @Test
    public void testIsMountedSdCardPath() {
        //before sd insert
        StorageMonitor.updateSubSystemStorageState(mContext);
        Assert.assertFalse(StorageMonitor.isMountedSdCardPath(AppConstants.EXT_SD_CARD_ROOT));
        Assert.assertFalse(StorageMonitor.isMountedSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "abc"));
        Assert.assertFalse(StorageMonitor.isMountedSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "/abc"));

        //after sd insert
        ShadowStorageManager.addStorageVolumes(AppConstants.EXT_SD_CARD_ROOT);
        StorageMonitor.updateSubSystemStorageState(mContext);
        Assert.assertTrue(StorageMonitor.isMountedSdCardPath(AppConstants.EXT_SD_CARD_ROOT));
        Assert.assertTrue(StorageMonitor.isMountedSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "/abc"));
        Assert.assertFalse(StorageMonitor.isMountedSdCardPath(AppConstants.EXT_SD_CARD_ROOT + "abc"));
    }

    @Test
    public void testIsRemovedExtSDCard() {
        //before sd insert
        StorageMonitor.updateSubSystemStorageState(mContext);
        Assert.assertTrue(StorageMonitor.isRemovedExtSDCard(AppConstants.EXT_SD_CARD_ROOT));

        //after sd insert
        ShadowStorageManager.addStorageVolumes(AppConstants.EXT_SD_CARD_ROOT);
        StorageMonitor.updateSubSystemStorageState(mContext);
        Assert.assertFalse(StorageMonitor.isRemovedExtSDCard(AppConstants.EXT_SD_CARD_ROOT));
    }

    @Test
    public void testGetStorageFreeSpace() {
        long size = StorageMonitor.getStorageFreeSpace(mContext, StorageMonitor.iStorageType.INTERNAL);
        Assert.assertEquals(ShadowStatFs.AVAILABLE_BYTE_SIZE, size);
    }

    @Test
    public void testGetDeviceStorageFreeSpace() {
        long size = StorageMonitor.getDeviceStorageFreeSpace();
        Assert.assertEquals(ShadowStatFs.AVAILABLE_BYTE_SIZE, size);
    }

    @Test
    public void testGetStorageSize() {
        final long FOUR_GIGA_BYTE = 1024L * 1024L * 1024L * 4L;
        long size = StorageMonitor.getInternalTotalSize(mContext);;
        Assert.assertEquals(FOUR_GIGA_BYTE, size);
    }

    @Test
    public void testGetStorageDisplayName() {
        String expected = mContext.getString(UiUtils.getInternalStorageRootResId());
        String actual = StorageMonitor.getStorageDisplayName(mContext, AppConstants.StoragePath.INTERNAL_ROOT);
        Assert.assertEquals(expected, actual);

        expected = "test.txt";
        actual = StorageMonitor.getStorageDisplayName(mContext, AppConstants.StoragePath.INTERNAL_ROOT + "/" + expected);
        Assert.assertEquals(expected, actual);


        expected = mContext.getString(R.string.sd_card);
        actual = StorageMonitor.getStorageDisplayName(mContext, StorageMonitor.getExtSDCardPath());
        Assert.assertEquals(expected, actual);

        expected = "test.txt";
        actual = StorageMonitor.getStorageDisplayName(mContext, StorageMonitor.getExtSDCardPath() + "/" + expected);
        Assert.assertEquals(expected, actual);
    }

    @Test
    public void testIsRemovableStoragePath() {
        TestInput<Boolean>[] testList = TestInput.createTestData(
                new TestInput<>(true, AppConstants.StoragePath.PRIVATE_ROOT),
                new TestInput<>(true, StorageMonitor.getExtSDCardPath()),
                new TestInput<>(true, "/mnt/usbA"),

                new TestInput<>(true, AppConstants.StoragePath.PRIVATE_ROOT + "/test"),
                new TestInput<>(true, StorageMonitor.getExtSDCardPath() + "/test"),
                new TestInput<>(true, "/mnt/usbA/test"),

                new TestInput<>(false, AppConstants.StoragePath.PRIVATE_ROOT + "NONE"),
                new TestInput<>(false, StorageMonitor.getExtSDCardPath() + "NONE"),
                new TestInput<>(false, "/mnt/usbANONE")
        );

        ShadowStorageManager.addStorageVolumes(AppConstants.EXT_SD_CARD_ROOT);
        ShadowStorageManager.addStorageVolumes("/mnt/usbA");
        StorageMonitor.updateSubSystemStorageState(mContext);

        for (TestInput<Boolean> test : testList) {
            Boolean actual = StorageMonitor.isCurrentStoragePath((String) test.get(0));
            Assert.assertEquals((String) test.get(0), test.expected(), actual);
        }
    }
}
